Skip to content

Conversation

@holger-waschke
Copy link
Contributor

@holger-waschke holger-waschke commented Oct 21, 2025

This change allows disabling the update of issue descriptions.

It´s a non breaking change the default is still to update everything. Only if you set the parameter disable_update_description explicit to true the description won´t get updated anymore.

We need this feature as there a lot of jira automations which updates the description itself, jiralert would revert the changes back to the original templated description state once the repeat_interval kicks in and the alert is still active, so it´s handy to have the possibility to disable it.

I added a new unit test for this function to test.

Exempel config

receivers:
- name: jira_alarm
  jira_configs:
  - project: MON
    issue_type: Bug
    resolve_transition: "Geschlossen"
    reopen_transition: "Erfasst"
    reopen_duration: "1h"
    disable_update_description: true

@cjedro
Copy link
Contributor

cjedro commented Oct 27, 2025

this is also important due to upcoming rate-limiting https://developer.atlassian.com/cloud/jira/platform/rate-limiting/

@holger-waschke
Copy link
Contributor Author

this is also important due to upcoming rate-limiting https://developer.atlassian.com/cloud/jira/platform/rate-limiting/

tbh this sounds to me like it will be mandatory to have some kind of back off mechanism to avoid running into these enforced rate limits?

@cjedro
Copy link
Contributor

cjedro commented Oct 28, 2025

@holger-waschke i think this is already implemented

retrier: &notify.Retrier{RetryCodes: []int{http.StatusTooManyRequests}},

@fluktuid
Copy link

fluktuid commented Oct 29, 2025

I'm with you, but this handles every alarm separate.
So in the worst-case when many alarms are open and firing, the rate-limit could result in no new alarms opened.
This could happen because of the push-mechanism that re-sends old alarms for updates.

I think generally an optional circuit-breaker could solve this.
Spontaneously I would think about two queues:

  1. more important (e.g. new alarms)
  2. less important (e.g. update/close alarms)

Maybe we should discuss this in an issue.

@Spaceman1701
Copy link
Contributor

I like this idea, but I'm wondering if we should have a more generic implementation. I'm less familiar with the Jira integration than I should be, honestly. Are there any other fields which we might want to avoid updating on repeat notifications? I'm wondering if we should allow fields to have sub-keys that indicate if we should update them or not.

I also think the discussion about rate-limiting is important enough to move to a dedicated issue. It's really a problem for any receiver, so it's something we should ideally solve generically.

@holger-waschke
Copy link
Contributor Author

holger-waschke commented Nov 4, 2025

I like this idea, but I'm wondering if we should have a more generic implementation. I'm less familiar with the Jira integration than I should be, honestly. Are there any other fields which we might want to avoid updating on repeat notifications? I'm wondering if we should allow fields to have sub-keys that indicate if we should update them or not.

I also think the discussion about rate-limiting is important enough to move to a dedicated issue. It's really a problem for any receiver, so it's something we should ideally solve generically.

we´re using this Jira API Endpoint to update existing issues
https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-put

In the request body we send the fields we would like to update. which are the same as creating a new issue so basically everything.
from my experience what should be configurable (whats most relevant to users) is the summary and description. customfields, priority and other stuff is more static in most cases. (for example customfield hostname or environment will not change)

@Spaceman1701
Copy link
Contributor

from my experience what should be configurable (whats most relevant to users) is the summary and description. customfields, priority and other stuff is more static in most cases. (for example customfield hostname or environment will not change)

That makes sense to me... The thing I'm worried about is that we'll end up having multiple disable_update_* keys floating around (which seems a little ugly to me).

What do you think about introducing a new JiraField type in the config that we can either deserialize from string (to maintain backwards compatibility) or from an object with keys that control updating? It's a bit more complexity, but I think it's a bit cleaner in the long run.

This would result in config like:

receivers:
- name: jira_alarm
  jira_configs:
  - project: MON
    issue_type: Bug
    resolve_transition: "Geschlossen"
    reopen_transition: "Erfasst"
    reopen_duration: "1h"
    description:
      disable_update: true

We could then reuse the type for summary or any other field that might end up needing this behavior.

cc @siavashs and @grobinson-grafana since they might have a different opinion.

@holger-waschke
Copy link
Contributor Author

@Spaceman1701
I implemented your suggestion. The unmarshalling remains backward-compatible, so nothing breaks — both formats are now supported:

# Old/legacy format (just a string)
summary: '{{ template "jira.default.summary" . }}'
description: '{{ template "jira.default.description" . }}'
# New format (object with fields)
summary:
  template: '{{ template "jira.default.summary" . }}'
  disable_update: true
description:
  template: '{{ template "jira.default.description" . }}'
  disable_update: true

The jira_test required a fair amount of refactoring to accommodate this, but it feels justified.

@holger-waschke holger-waschke force-pushed the jira_disable_update_per_config branch from c81ed9c to 9da06dc Compare November 14, 2025 20:24
Copy link
Contributor

@Spaceman1701 Spaceman1701 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making those changes! I like the implementation, this is looking good to me.

Last thing is to make sure the docs reflect this change.

Project *issueProject `json:"project,omitempty"`
Resolution *idNameValue `json:"resolution,omitempty"`
Summary string `json:"summary"`
Summary any `json:"summary"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this change? I see that Description is also any, so I think this is fine. Just answering here in github is enough that someone will be able to learn the answer if they come looking for it in the future 😄

Copy link
Contributor Author

@holger-waschke holger-waschke Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any is an alias for interface{} and we´re using it in combination with the MarshalJSON function

// MarshalJSON merges the struct issueFields and issueFields.CustomField together.
func (i issueFields) MarshalJSON() ([]byte, error) {
	jsonFields := map[string]interface{}{}

	if i.Summary != nil {
		jsonFields["summary"] = i.Summary
	}

	if i.Description != nil {
		jsonFields["description"] = i.Description
	}

to ensure that the field is nil and not an empty string when updating existing issues with the fields not being included. parsing an empty string would just update the field to an empty string so we have to make sure it´s omitted from the JSON completely. I´m not quite sure if a string pointer could be used here as well I tested it and I think it would need quite some reworking.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We try to avoid using any or interface{} in the configuration file. Instead, we tend to work on the basis that an empty string means no value. Why can't we just do if i.Summary != ""?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm inclined to agree here. And if the intent is "a string or nil", I think think using a *string expresses that much better than any. Otherwise, we're opening ourselves up to future changes introducing mistyped fields in jira requests.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! Use *string if you want empty string to be a valid value instead of empty, otherwise use string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

somehow anticipated this change request and I yes I agree with you :-) . Changed it to use *string. Added little helper to the unit test

func stringPtr(v string) *string {
	return &v
}

@Spaceman1701 Spaceman1701 self-assigned this Nov 14, 2025
@holger-waschke holger-waschke force-pushed the jira_disable_update_per_config branch 3 times, most recently from c80b5fa to 14e532a Compare November 17, 2025 13:46
@holger-waschke
Copy link
Contributor Author

holger-waschke commented Nov 17, 2025

Built in more improvements

  • improved log output when updating existing issues debug output will now print out which labels are being skipped, e.g.

level=DEBUG source=jira.go:119 msg="updating existing issue without summary and description" integration=jira group_key="{}/{test="true"}:{alertname="E2E Failed Application", environment="Abnahme"}" issue_key=ASPMIG-447810

  • improved JiraFieldConfig unmarshaling to respect the template field default. Setting summary and description is fully optional. you can have it like this or leave it full out.
- name: jira_asp_alarm
  jira_configs:
  - project: ASPMIG
    issue_type: Alarm
    summary:
      template: '{{ template "jira.default.summary" . }}'
    description:
      disable_update: true

new logic for the atlassian v3 api endpoint. (which uses atlassian document format). I had to rewrite this because of the string pointer anyway so It´s optimized for only check for valid JSON. The actual encoding takes place in the doAPIRequestFullPath function anyway.

	descriptionCopy := issueDescriptionString
	if isAPIv3Path(n.conf.APIURL.Path) {
		if !json.Valid([]byte(descriptionCopy)) {
			return issue{}, fmt.Errorf("description template: invalid JSON for API v3")
		}
	}
	requestBody.Fields.Description = &descriptionCopy

However, v3 endpoint with atlassian document format is broken atm, but this is another issue I´ll reply to #4585. This needs quite some refactoring, too.

Copy link
Contributor

@Spaceman1701 Spaceman1701 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making these changes! One small thing in the new logging code, but otherwise LGTM.

@holger-waschke holger-waschke force-pushed the jira_disable_update_per_config branch from ed1337d to 8a7cbdb Compare November 17, 2025 15:47
Copy link
Contributor

@Spaceman1701 Spaceman1701 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ultrotter
Copy link
Contributor

@SuperQ mentioned one last change he would like: could we have the flag be positive?
i.e. change disable_update_description: true to update_description: false ?

Thanks!

@holger-waschke
Copy link
Contributor Author

@SuperQ mentioned one last change he would like: could we have the flag be positive? i.e. change disable_update_description: true to update_description: false ?

Thanks!

sure, however it´s a breaking change then because the default behavior will change from update everything to NOT update summary / description if not explicit set otherwise. is that ok for you?

@grobinson-grafana
Copy link
Collaborator

@SuperQ mentioned one last change he would like: could we have the flag be positive? i.e. change disable_update_description: true to update_description: false ?
Thanks!

sure, however it´s a breaking change then because the default behavior will change from update everything to NOT update summary / description if not explicit set otherwise. is that ok for you?

What about update_description: true as the default?

@SuperQ
Copy link
Member

SuperQ commented Nov 18, 2025

Yes, my suggestion was to change the naming / default from disable_update_description: false to update_description: true.

This avoids the false: false double negative.

@holger-waschke
Copy link
Contributor Author

Yes, my suggestion was to change the naming / default from disable_update_description: false to update_description: true.

This avoids the false: false double negative.

I see. But since it´s a bool and defaults to false how exactly you want to make it default to true?
Use a pointer an threat nil as true with a helper function
Or extend the UnmarshalYAML with a default?

@SuperQ
Copy link
Member

SuperQ commented Nov 18, 2025

Take a look at the Prometheus config/config.go.

https://github.com/prometheus/prometheus/blob/main/config/config.go#L192-L200

@holger-waschke holger-waschke force-pushed the jira_disable_update_per_config branch 2 times, most recently from d0bf3b0 to d2dd0cf Compare November 18, 2025 15:39
@holger-waschke holger-waschke force-pushed the jira_disable_update_per_config branch from 491124b to 16a4342 Compare November 18, 2025 15:42
@holger-waschke
Copy link
Contributor Author

Take a look at the Prometheus config/config.go.

https://github.com/prometheus/prometheus/blob/main/config/config.go#L192-L200

Played around a bit and came up with this solution.
use a pointer on the bool and a little helper function

func (f *JiraFieldConfig) EnableUpdateValue() bool {
	if f.EnableUpdate == nil {
		return true
	}
	return *f.EnableUpdate
}

@SuperQ SuperQ merged commit a90da79 into prometheus:main Nov 18, 2025
7 checks passed
@holger-waschke holger-waschke deleted the jira_disable_update_per_config branch November 18, 2025 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants